package com.duobaoyu.dby.supply.admin.aspect;

import com.alibaba.nacos.common.utils.HttpMethod;
import com.duobaoyu.dby.channel.common.util.ToolUtils;
import com.duobaoyu.dby.common.core.constant.CommonConstants;
import com.duobaoyu.dby.common.core.constant.RequestConstants;
import com.duobaoyu.dby.common.core.user.DbyUser;
import com.duobaoyu.dby.common.core.util.ip.IpUtil;
import com.duobaoyu.dby.common.security.util.SecurityUtils;
import com.duobaoyu.dby.supply.admin.annotation.SupplySysLog;
import com.duobaoyu.dby.supply.admin.holder.SysLogHolder;
import com.duobaoyu.dby.supply.biz.producer.SupplyChainMQProducer;
import com.duobaoyu.dby.supply.core.common.util.DeflaterUtils;
import com.duobaoyu.dby.supply.db.entity.message.SystemLog;
import com.duobaoyu.dby.supply.db.util.OperateAuthUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;

/**
 * @author liyang
 * @version 1.0.0
 * @ClassName WebLogAspect
 * @Description 日志操作AOP
 * @createTime 16:47:00 2022/06/08
 */
@Slf4j
@Aspect
@Order(5)
@RequiredArgsConstructor
@Component
public class WebLogAspect {

	private final ObjectMapper objectMapper;
    private final SupplyChainMQProducer supplyChainMQProducer;

    @Pointcut("@annotation(com.duobaoyu.dby.supply.admin.annotation.SupplySysLog)")
    public void webLog() {
    }

	@SneakyThrows
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        SysLogHolder.setTime(System.currentTimeMillis());
		SystemLog systemLog = SysLogHolder.getSystemLog();
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        //HttpSession session = (HttpSession) attributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
        if (Objects.isNull(systemLog)) {
			systemLog = new SystemLog();
            SysLogHolder.setSysLog(systemLog);
        }
        systemLog.setRequestId(MDC.get(RequestConstants.REQUEST_ID));
		systemLog.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
		systemLog.setHttpMethod(request.getMethod());
        //获取传入目标方法的参数
        String str;
        if(Objects.equals(HttpMethod.GET,request.getMethod())){
            Map<String, String[]> parameterMap = request.getParameterMap();
            str = objectMapper.writeValueAsString(parameterMap);
        }else {
            Object[] args = joinPoint.getArgs();
            for (int i = 0; i < args.length; i++) {
                Object o = args[i];
                if (o instanceof ServletRequest || (o instanceof ServletResponse) || o instanceof MultipartFile) {
                    args[i] = o.toString();
                }
            }
            str = objectMapper.writeValueAsString(args);
        }
        str = DeflaterUtils.zipString(str);
		systemLog.setParams(str.length() > 5000 ? objectMapper.writeValueAsString("请求参数数据过长不与显示") : str);
        String ip = null;
        if (Objects.nonNull(request)) {
            ip = IpUtil.getIpAddr(request);
			systemLog.setIp(ip);
        }
		systemLog.setRequestUri(request.getRequestURL().toString());
		DbyUser user = SecurityUtils.getUser();
		if (Objects.nonNull(user) && Objects.nonNull(user.getId())) {
            systemLog.setOperationId(user.getId());
            systemLog.setOperationName(OperateAuthUtil.getAdministrator());
        } else {
			systemLog.setOperationId(Long.valueOf(CommonConstants.ZERO));
			systemLog.setOperationName("匿名用户");
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
		SupplySysLog sysLog = method.getAnnotation(SupplySysLog.class);
        if (Objects.nonNull(sysLog)) {
            //注解上的描述
			systemLog.setTitle(sysLog.value());
			systemLog.setOperationType(sysLog.operationType().getValue());
        }
        Map<String, String> browserMap = ToolUtils.getOsAndBrowserInfo(request);
		systemLog.setBrowser(browserMap.get("os") + "-" + browserMap.get("browser"));
        /*if (StringUtils.isNotBlank(ip) && !"127.0.0.1".equals(ip)) {
            Map<String, String> map = (Map<String, String>) session.getAttribute("addressIp");
            if (map == null) {
                map = ToolUtil.getAddressByIp(ToolUtil.getClientIp(request));
                session.setAttribute("addressIp", map);
            }
			systemLog.setArea(map.get("area"));
			systemLog.setProvince(map.get("province"));
			systemLog.setCity(map.get("city"));
			systemLog.setIsp(map.get("isp"));
        }*/
		systemLog.setType(ToolUtils.isAjax(request) ? "Ajax请求" : "普通请求");
    }

    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try {
			return proceedingJoinPoint.proceed();
        } catch (Exception e) {
        	log.warn("供应链接口调用失败！ex => {}", ExceptionUtils.getStackTrace(e));
			SystemLog systemLog = SysLogHolder.getSystemLog();
			systemLog.setException(e.getMessage());
			LocalDateTime now = LocalDateTime.now();
			systemLog.setCreateTime(now);
            systemLog.setCreateBy(String.valueOf(systemLog.getOperationId()));
            supplyChainMQProducer.sendSystemLogMessage(this.objectMapper.writeValueAsString(systemLog));
			throw e;
        }finally {
			SysLogHolder.removeSysLog();
			SysLogHolder.removeTime();
		}
    }

	@SneakyThrows
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) {
        String retString = objectMapper.writeValueAsString(ret);
		SystemLog systemLog = SysLogHolder.getSystemLog();
        retString = DeflaterUtils.zipString(retString);
		systemLog.setResponse(retString.length() > 5000 ? objectMapper.writeValueAsString("请求参数数据过长不与显示") : retString);
		systemLog.setUseTime(System.currentTimeMillis() - SysLogHolder.getTime());
        LocalDateTime now = LocalDateTime.now();
		systemLog.setCreateTime(now);
        systemLog.setCreateBy(String.valueOf(systemLog.getOperationId()));
        supplyChainMQProducer.sendSystemLogMessage(this.objectMapper.writeValueAsString(systemLog));
        SysLogHolder.removeSysLog();
        SysLogHolder.removeTime();
    }
}
